package cn.yemuysy.code.lingting.service;

import cn.yemuysy.code.lingting.core.AhoCorasickAutomaton;
import cn.yemuysy.code.lingting.core.CombinationMatcher;
import cn.yemuysy.code.lingting.core.PinyinMatcher;
import cn.yemuysy.code.lingting.model.MatchResult;
import cn.yemuysy.code.lingting.model.SensitiveWord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 统一敏感词检测服务
 * 整合普通敏感词、组合敏感词、拼音敏感词、拼音组合敏感词检测功能
 */
public class SensitiveWordDetector {
    
    private static final Logger logger = LoggerFactory.getLogger(SensitiveWordDetector.class);
    
    /**
     * 普通敏感词AC自动机
     */
    private AhoCorasickAutomaton normalAutomaton;
    
    /**
     * 组合敏感词匹配器
     */
    private CombinationMatcher combinationMatcher;
    
    /**
     * 拼音敏感词匹配器
     */
    private PinyinMatcher pinyinMatcher;
    
    /**
     * 拼音组合敏感词匹配器
     */
    private CombinationMatcher pinyinCombinationMatcher;
    
    /**
     * 拼音组合敏感词的拼音匹配器
     */
    private PinyinMatcher pinyinCombinationPinyinMatcher;
    
    /**
     * 检测配置
     */
    private DetectionConfig config;
    
    /**
     * 检测配置类
     */
    public static class DetectionConfig {
        private boolean enableNormal = true;
        private boolean enableCombination = true;
        private boolean enablePinyin = true;
        private boolean enablePinyinCombination = true;
        private boolean enableDuplicateFilter = true;
        private int maxResults = 1000;
        
        // Getters and Setters
        public boolean isEnableNormal() { return enableNormal; }
        public void setEnableNormal(boolean enableNormal) { this.enableNormal = enableNormal; }
        
        public boolean isEnableCombination() { return enableCombination; }
        public void setEnableCombination(boolean enableCombination) { this.enableCombination = enableCombination; }
        
        public boolean isEnablePinyin() { return enablePinyin; }
        public void setEnablePinyin(boolean enablePinyin) { this.enablePinyin = enablePinyin; }
        
        public boolean isEnablePinyinCombination() { return enablePinyinCombination; }
        public void setEnablePinyinCombination(boolean enablePinyinCombination) { this.enablePinyinCombination = enablePinyinCombination; }
        
        public boolean isEnableDuplicateFilter() { return enableDuplicateFilter; }
        public void setEnableDuplicateFilter(boolean enableDuplicateFilter) { this.enableDuplicateFilter = enableDuplicateFilter; }
        
        public int getMaxResults() { return maxResults; }
        public void setMaxResults(int maxResults) { this.maxResults = maxResults; }
    }
    
    public SensitiveWordDetector() {
        this.normalAutomaton = new AhoCorasickAutomaton(false);
        this.combinationMatcher = new CombinationMatcher();
        this.pinyinMatcher = new PinyinMatcher();
        this.pinyinCombinationMatcher = new CombinationMatcher();
        this.pinyinCombinationPinyinMatcher = new PinyinMatcher();
        this.config = new DetectionConfig();
    }
    
    public SensitiveWordDetector(DetectionConfig config) {
        this();
        this.config = config;
    }
    
    /**
     * 添加普通敏感词
     * @param word 敏感词
     */
    public void addNormalWord(String word) {
        if (word == null || word.trim().isEmpty()) {
            return;
        }
        normalAutomaton.addWord(word.trim());
        logger.debug("添加普通敏感词: {}", word);
    }
    
    /**
     * 批量添加普通敏感词
     * @param words 敏感词列表
     */
    public void addNormalWords(Collection<String> words) {
        if (words == null || words.isEmpty()) {
            return;
        }
        
        List<String> cleanWords = words.stream()
                .filter(Objects::nonNull)
                .map(String::trim)
                .filter(w -> !w.isEmpty())
                .collect(Collectors.toList());
        
        normalAutomaton.addWords(cleanWords);
        logger.info("批量添加普通敏感词完成，总计: {} 个", cleanWords.size());
    }
    
    /**
     * 添加组合敏感词
     * @param combinationStr 组合字符串，格式：澳门+博彩+网站
     */
    public void addCombinationWord(String combinationStr) {
        if (combinationStr == null || combinationStr.trim().isEmpty()) {
            return;
        }
        combinationMatcher.addCombinationFromString(combinationStr.trim());
        logger.debug("添加组合敏感词: {}", combinationStr);
    }
    
    /**
     * 批量添加组合敏感词
     * @param combinationStrs 组合字符串列表
     */
    public void addCombinationWords(Collection<String> combinationStrs) {
        if (combinationStrs == null || combinationStrs.isEmpty()) {
            return;
        }
        
        for (String combinationStr : combinationStrs) {
            if (combinationStr != null && !combinationStr.trim().isEmpty()) {
                addCombinationWord(combinationStr);
            }
        }
        logger.info("批量添加组合敏感词完成，总计: {} 个", combinationStrs.size());
    }
    
    /**
     * 添加拼音敏感词
     * @param word 敏感词
     */
    public void addPinyinWord(String word) {
        if (word == null || word.trim().isEmpty()) {
            return;
        }
        pinyinMatcher.addPinyinWord(word.trim());
        logger.debug("添加拼音敏感词: {}", word);
    }
    
    /**
     * 批量添加拼音敏感词
     * @param words 敏感词列表
     */
    public void addPinyinWords(Collection<String> words) {
        if (words == null || words.isEmpty()) {
            return;
        }
        
        List<String> cleanWords = words.stream()
                .filter(Objects::nonNull)
                .map(String::trim)
                .filter(w -> !w.isEmpty())
                .collect(Collectors.toList());
        
        pinyinMatcher.addPinyinWords(cleanWords);
        logger.info("批量添加拼音敏感词完成，总计: {} 个", cleanWords.size());
    }
    
    /**
     * 添加拼音组合敏感词
     * @param combinationStr 组合字符串，格式：澳门+博彩+网站
     */
    public void addPinyinCombinationWord(String combinationStr) {
        if (combinationStr == null || combinationStr.trim().isEmpty()) {
            return;
        }
        
        SensitiveWord combination = SensitiveWord.parseCombinationWord(combinationStr.trim());
        
        // 添加到拼音组合匹配器的组合管理
        pinyinCombinationMatcher.addCombination(combination);
        
        // 将组合的各个组成部分添加到拼音匹配器
        for (String part : combination.getParts()) {
            pinyinCombinationPinyinMatcher.addPinyinWord(part);
        }
        
        logger.debug("添加拼音组合敏感词: {}", combinationStr);
    }
    
    /**
     * 批量添加拼音组合敏感词
     * @param combinationStrs 组合字符串列表
     */
    public void addPinyinCombinationWords(Collection<String> combinationStrs) {
        if (combinationStrs == null || combinationStrs.isEmpty()) {
            return;
        }
        
        for (String combinationStr : combinationStrs) {
            if (combinationStr != null && !combinationStr.trim().isEmpty()) {
                addPinyinCombinationWord(combinationStr);
            }
        }
        logger.info("批量添加拼音组合敏感词完成，总计: {} 个", combinationStrs.size());
    }
    
    /**
     * 检测文本中的所有敏感词
     * @param text 要检测的文本
     * @return 匹配结果列表
     */
    public List<MatchResult> detectAll(String text) {
        if (text == null || text.trim().isEmpty()) {
            return new ArrayList<>();
        }
        
        List<MatchResult> allResults = new ArrayList<>();
        long startTime = System.currentTimeMillis();
        
        try {
            // 1. 普通敏感词检测
            if (config.isEnableNormal()) {
                List<MatchResult> normalResults = normalAutomaton.search(text);
                allResults.addAll(normalResults);
                logger.debug("普通敏感词检测完成，匹配数量: {}", normalResults.size());
            }
            
            // 2. 组合敏感词检测
            if (config.isEnableCombination()) {
                List<MatchResult> combinationResults = combinationMatcher.searchCombinations(text);
                allResults.addAll(combinationResults);
                logger.debug("组合敏感词检测完成，匹配数量: {}", combinationResults.size());
            }
            
            // 3. 拼音敏感词检测
            if (config.isEnablePinyin()) {
                List<MatchResult> pinyinResults = pinyinMatcher.searchPinyinWords(text);
                allResults.addAll(pinyinResults);
                logger.debug("拼音敏感词检测完成，匹配数量: {}", pinyinResults.size());
            }
            
            // 4. 拼音组合敏感词检测
            if (config.isEnablePinyinCombination()) {
                List<MatchResult> pinyinCombinationResults = detectPinyinCombinations(text);
                allResults.addAll(pinyinCombinationResults);
                logger.debug("拼音组合敏感词检测完成，匹配数量: {}", pinyinCombinationResults.size());
            }
            
            // 5. 结果处理
            if (config.isEnableDuplicateFilter()) {
                allResults = removeDuplicates(allResults);
            }
            
            // 6. 限制结果数量
            if (allResults.size() > config.getMaxResults()) {
                allResults = allResults.subList(0, config.getMaxResults());
                logger.warn("检测结果数量超过限制，已截取前 {} 个结果", config.getMaxResults());
            }
            
            // 7. 按位置排序
            allResults.sort(Comparator.comparingInt(MatchResult::getStartIndex));
            
        } catch (Exception e) {
            logger.error("敏感词检测发生错误", e);
            throw new RuntimeException("敏感词检测失败", e);
        }
        
        long endTime = System.currentTimeMillis();
        logger.info("敏感词检测完成，文本长度: {}, 匹配数量: {}, 耗时: {}ms", 
                   text.length(), allResults.size(), (endTime - startTime));
        
        return allResults;
    }
    
    /**
     * 检测拼音组合敏感词
     */
    private List<MatchResult> detectPinyinCombinations(String text) {
        // 先使用拼音匹配器找出所有拼音敏感词
        List<MatchResult> pinyinMatches = pinyinCombinationPinyinMatcher.searchPinyinWords(text);
        
        if (pinyinMatches.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 然后检查这些匹配是否形成组合
        return checkPinyinCombinationMatches(pinyinMatches, text);
    }
    
    /**
     * 检查拼音组合匹配
     */
    private List<MatchResult> checkPinyinCombinationMatches(List<MatchResult> pinyinMatches, String text) {
        List<MatchResult> results = new ArrayList<>();
        
        // 按组合分组统计
        Map<String, Set<String>> combinationMatches = new HashMap<>();
        
        for (MatchResult match : pinyinMatches) {
            String matchedWord = match.getWord();
            
            // 查找包含此词的所有拼音组合
            for (SensitiveWord combination : pinyinCombinationMatcher.getAllCombinations()) {
                if (combination.containsPart(matchedWord)) {
                    String combinationId = combination.getWord();
                    combinationMatches.computeIfAbsent(combinationId, k -> new HashSet<>()).add(matchedWord);
                }
            }
        }
        
        // 检查组合是否完全匹配
        for (Map.Entry<String, Set<String>> entry : combinationMatches.entrySet()) {
            String combinationId = entry.getKey();
            Set<String> matchedParts = entry.getValue();
            
            SensitiveWord combination = pinyinCombinationMatcher.getCombination(combinationId);
            if (combination != null && combination.isFullyMatched(new ArrayList<>(matchedParts))) {
                // 计算位置信息
                List<MatchResult> relatedMatches = pinyinMatches.stream()
                        .filter(m -> matchedParts.contains(m.getWord()))
                        .collect(Collectors.toList());
                
                int startIndex = relatedMatches.stream().mapToInt(MatchResult::getStartIndex).min().orElse(0);
                int endIndex = relatedMatches.stream().mapToInt(MatchResult::getEndIndex).max().orElse(text.length() - 1);
                
                MatchResult result = new MatchResult(combinationId, startIndex, endIndex, 
                                                   MatchResult.MatchType.PINYIN_COMBINATION);
                
                for (String part : matchedParts) {
                    result.addCombinationPart(part);
                }
                
                results.add(result);
            }
        }
        
        return results;
    }
    
    /**
     * 移除重复的匹配结果
     */
    private List<MatchResult> removeDuplicates(List<MatchResult> results) {
        if (results.isEmpty()) {
            return results;
        }
        
        // 使用自定义去重逻辑，优先保留匹配长度更长的结果
        Map<String, MatchResult> uniqueResults = new HashMap<>();
        
        for (MatchResult result : results) {
            String key = result.getStartIndex() + "-" + result.getEndIndex();
            MatchResult existing = uniqueResults.get(key);
            
            if (existing == null || result.getWord().length() > existing.getWord().length()) {
                uniqueResults.put(key, result);
            }
        }
        
        return new ArrayList<>(uniqueResults.values());
    }
    
    /**
     * 检查文本是否包含敏感词
     * @param text 要检查的文本
     * @return true如果包含敏感词
     */
    public boolean containsSensitiveWord(String text) {
        List<MatchResult> results = detectAll(text);
        return !results.isEmpty();
    }
    
    /**
     * 获取简化的检测结果（仅包含敏感词）
     * @param text 要检测的文本
     * @return 敏感词列表
     */
    public List<String> getSensitiveWords(String text) {
        return detectAll(text).stream()
                .map(MatchResult::getWord)
                .distinct()
                .collect(Collectors.toList());
    }
    
    /**
     * 敏感词统计信息
     */
    public Map<String, Integer> getStatistics() {
        Map<String, Integer> stats = new HashMap<>();
        stats.put("普通敏感词", normalAutomaton.getWordCount());
        stats.put("组合敏感词", combinationMatcher.getCombinationCount());
        stats.put("拼音敏感词", pinyinMatcher.getPinyinWordCount());
        stats.put("拼音组合敏感词", pinyinCombinationMatcher.getCombinationCount());
        
        int total = stats.values().stream().mapToInt(Integer::intValue).sum();
        stats.put("总计", total);
        
        return stats;
    }
    
    /**
     * 清空所有敏感词
     */
    public void clear() {
        normalAutomaton.clear();
        combinationMatcher.clear();
        pinyinMatcher.clear();
        pinyinCombinationMatcher.clear();
        pinyinCombinationPinyinMatcher.clear();
        logger.info("敏感词检测器已清空");
    }
    
    // Getters and Setters
    public DetectionConfig getConfig() {
        return config;
    }
    
    public void setConfig(DetectionConfig config) {
        this.config = config;
    }
    
    @Override
    public String toString() {
        Map<String, Integer> stats = getStatistics();
        return "SensitiveWordDetector{" +
                "normalWords=" + stats.get("普通敏感词") +
                ", combinationWords=" + stats.get("组合敏感词") +
                ", pinyinWords=" + stats.get("拼音敏感词") +
                ", pinyinCombinationWords=" + stats.get("拼音组合敏感词") +
                ", total=" + stats.get("总计") +
                ", config=" + config +
                '}';
    }
} 